home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / pcr / pcr4_4.lha / DIST / gc / GCreclaim.c < prev    next >
C/C++ Source or Header  |  1992-02-25  |  36KB  |  1,251 lines

  1. /* begincopyright
  2.   Copyright (c) 1988,1990 Xerox Corporation. All rights reserved.
  3.   Use and copying of this software and preparation of derivative works based
  4.   upon this software are permitted. Any distribution of this software or
  5.   derivative works must comply with all applicable United States export
  6.   control laws. This software is made available AS IS, and Xerox Corporation
  7.   makes no warranty about the software, its performance or its conformity to
  8.   any specification. Any person obtaining a copy of this software is requested
  9.   to send their name and post office or electronic mail address to:
  10.     PCR Coordinator
  11.     Xerox PARC
  12.     3333 Coyote Hill Rd.
  13.     Palo Alto, CA 94304
  14.     
  15.   Parts of this software were derived from code bearing the copyright notice:
  16.   
  17.   Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
  18.   This material may be freely distributed, provided this notice is retained.
  19.   This material is provided as is, with no warranty expressed or implied.
  20.   Use at your own risk.
  21.   
  22.   endcopyright */
  23. #define CHECK_INVARIANTS
  24. #undef CHECK_INVARIANTS
  25. #include "xr/GCPrivate.h"
  26. #include "xr/Threads.h"
  27.  
  28. #define I_HOLD_ML(ml) (((XR_ML)(ml))->ml_holder == XR_currThread)
  29.  
  30. #define DEBUG
  31. #undef DEBUG
  32.  
  33. /* Boehm, October 8, 1990 1:57:26 pm PDT */
  34.  
  35.  
  36.  
  37. /*
  38.  * reclaim phase
  39.  *
  40.  */
  41.  
  42. static long GC_last_gc_flushed = 0;
  43.  
  44. static struct hblk * GC_freeable_blocks = 0;
  45.     /* List of large object blocks waiting to be freed.  We enqueue these */
  46.     /* and then process them in large chunks.  Freeing a heap block can   */
  47.     /* be sufficiently expensive that we don't want to do this with the   */
  48.     /* world stopped. Processing them one at a time slows things down     */
  49.     /* because it causes the heap block allocator to lose some state.     */
  50.  
  51. /* Free some or all blocks on list of freeable blocks. Caller should hold */
  52. /* GC_allocate_ml.                               */
  53. static GC_free_blocks(how_many)
  54. long how_many;
  55. # define ALL 1000000000
  56. # define A_FEW 10
  57. {
  58.     register int i;
  59.     register struct hblk * h;
  60.     
  61.     if (!I_HOLD_ML(&GC_allocate_ml)) {
  62.         XR_Panic("GC_free_blocks 0");
  63.     }
  64.     for (i = 0; i < how_many; i++) {
  65.         if ((h = GC_freeable_blocks) == 0) return;
  66.         GC_freeable_blocks = hb_sz_link(h);
  67.         /* Mark it as being uninitialized */
  68.         hb_uninit(h) = 1;
  69.  
  70.     /* remove this block from list of active blocks */
  71.         GC_del_hblklist(h);
  72.         
  73.     GC_freehblk(h);
  74.     }
  75. }
  76.     
  77. /* Assumes caller holds GC_allocate_ml */
  78. void GC_zero_reclaim_lists()
  79. {
  80.     register struct hblk ** hbpp;
  81.     register struct hblk ** rlp;
  82.     register struct hblk * hbp;
  83.     
  84.     for( rlp = reclaim_list; rlp < &reclaim_list[MAXOBJSZ+1]; rlp++ ) {
  85.       *rlp = (struct hblk *)0;
  86.     }
  87.     for( rlp = areclaim_list; rlp < &areclaim_list[MAXOBJSZ+1]; rlp++ ) {
  88.       *rlp = (struct hblk *)0;
  89.     }
  90.     GC_reclaim_inval_counter++;
  91. }
  92.  
  93. void GC_reclaim_empty_pages()
  94. /* Reclaim any potentially empty blocks that have not yet been     */
  95. /* reclaimed.                               */
  96. /* Caller should hold GC_allocate_ml.                   */
  97. {
  98.     register struct hblk ** hbpp;
  99.     register struct hblk ** rlp;
  100.     register struct hblk * hbp;
  101. #   ifdef PRINTSTATS
  102.     register long n_reclaimed = 0;
  103.     register long n_tenured = 0;
  104. #   endif
  105.  
  106.     if (!I_HOLD_ML(&GC_allocate_ml)) {
  107.         XR_Panic("GC_reclaim_empty_pages 0");
  108.     }
  109.     GC_free_blocks(ALL);
  110.     if (GC_gc_no == GC_last_gc_flushed) {
  111.     return;
  112.     }
  113.  
  114.     /* First blocks with composite objects: */
  115.       for( rlp = reclaim_list; rlp < &reclaim_list[MAXOBJSZ+1]; rlp++ ) {
  116.      hbpp = rlp;
  117.     while ((hbp = *hbpp) != (struct hblk *)0) {
  118.         if (GC_hblk_empty(hbp)) {
  119. #               ifdef PRINTSTATS
  120.             n_reclaimed++;
  121. #               endif
  122.         * hbpp = hb_sz_link(hbp);
  123.         GC_reclaim_entire_block(hbp);
  124.         } else {
  125.         hbpp = & (hb_sz_link(hbp));
  126. #               ifdef PRINTSTATS
  127.             n_tenured++;
  128. #               endif
  129.         }
  130.     }
  131.       }
  132.  
  133.     /* Now blocks with atomic objects: */
  134.       for( rlp = areclaim_list; rlp < &areclaim_list[MAXOBJSZ+1]; rlp++ ) {
  135.     hbpp = rlp;
  136.     while ((hbp = *hbpp) != (struct hblk *)0) {
  137.        if (GC_hblk_empty(hbp)) {
  138. #               ifdef PRINTSTATS
  139.             n_reclaimed++;
  140. #               endif
  141.         * hbpp = hb_sz_link(hbp);
  142.         GC_reclaim_entire_block(hbp);
  143.         } else {
  144.         hbpp = & (hb_sz_link(hbp));
  145. #               ifdef PRINTSTATS
  146.             n_tenured++;
  147. #               endif
  148.         }
  149.     }
  150.       }
  151.  
  152.     GC_last_gc_flushed = GC_gc_no;
  153.  
  154. #   ifdef PRINTSTATS
  155.         GC_vprintf(
  156.        "Reclaimed %d apparently empty blocks, skipped %d nonempty blocks\n",
  157.        n_reclaimed, n_tenured);
  158. #   endif
  159. }
  160.  
  161. /*
  162.  * Reclaim all pages that were reclaimed in the last collection cycle.
  163.  * Flush the rest.
  164.  *
  165.  * GC_allocate_ml should not be held by the caller.  However,
  166.  * we will acquire and release it repeatedly.
  167.  * We claim that we don't need the allocation lock for most of this,
  168.  * since GC_reclaim_composite and GC_reclaim_atomic are robust
  169.  * against being called on an empty list, and we don't write to any
  170.  * important data structures.  Thus we can't screw up in any
  171.  * way that would affect correctness (other than of printed
  172.  * statistics).  We do guarantee that all reclaim lists are
  173.  * empty when we're done, since no other thread will be adding
  174.  * to the lists.
  175.  */
  176. void GC_reclaim_useful_pages()
  177. {
  178.     register struct hblk ** hbpp;
  179.     register struct hblk * hbp;
  180.     register struct hblk ** rlp;
  181. #   ifdef PRINTSTATS
  182.     register long n_reclaimed = 0;
  183.     register long n_tenured = 0;
  184. #   endif
  185.  
  186.     if (I_HOLD_ML(&GC_allocate_ml)) {
  187.         XR_Panic("GC_reclaim_useful_pages 0");
  188.     }
  189.     /* First blocks with composite objects: */
  190.       for( rlp = reclaim_list; rlp < &reclaim_list[MAXOBJSZ+1]; rlp++ ) {
  191.     while ((hbp = *rlp) != (struct hblk *)0) {
  192.         if (hb_last_reclaimed(hbp) == GC_gc_no-1
  193.             || GC_hblk_empty(hbp)) {
  194. #               ifdef PRINTSTATS
  195.             n_reclaimed++;
  196. #               endif
  197.         GC_reclaim_composite(rlp);
  198.         } else {
  199. #               ifdef PRINTSTATS
  200.             n_tenured++;
  201. #               endif
  202.         XR_MonitorEntry(&GC_allocate_ml);
  203.         *rlp = hb_sz_link(hbp);
  204.         XR_MonitorExit(&GC_allocate_ml);
  205.         }
  206.     }
  207.       }
  208.  
  209.     /* Now blocks with atomic objects: */
  210.       for( rlp = areclaim_list; rlp < &areclaim_list[MAXOBJSZ+1]; rlp++ ) {
  211.     hbpp = rlp;
  212.     while ((hbp = *rlp) != (struct hblk *)0) {
  213.        if (hb_last_reclaimed(hbp) == GC_gc_no-1
  214.            || GC_hblk_empty(hbp)) {
  215. #               ifdef PRINTSTATS
  216.             n_reclaimed++;
  217. #               endif
  218.         GC_reclaim_atomic(rlp);
  219.         } else {
  220. #               ifdef PRINTSTATS
  221.             n_tenured++;
  222. #               endif
  223.         XR_MonitorEntry(&GC_allocate_ml);
  224.         *rlp = hb_sz_link(hbp);
  225.         XR_MonitorExit(&GC_allocate_ml);
  226.         }
  227.     }
  228.       }
  229.  
  230.     XR_MonitorEntry(&GC_allocate_ml);
  231.     GC_last_gc_flushed = GC_gc_no;
  232.     GC_reclaim_inval_counter++;
  233.     XR_MonitorExit(&GC_allocate_ml);
  234.  
  235.     GC_vprintf( "Reclaimed %d new blocks, flushed %d old blocks\n",
  236.            n_reclaimed, n_tenured);
  237. }
  238.  
  239.  
  240. /*
  241.  * The main routine for the sweep phase
  242.  */
  243.  
  244. void GC_clear_free_lists()
  245. {
  246.   /* Clear all free lists */
  247.   register struct obj **fop;
  248.   
  249.   for( fop = objfreelist; fop < &objfreelist[MAXOBJSZ+1]; fop++ ) {
  250.     *fop = (struct obj *)0;
  251.   }
  252.   for( fop = aobjfreelist; fop < &aobjfreelist[MAXOBJSZ+1]; fop++ ) {
  253.     *fop = (struct obj *)0;
  254.   }
  255. }
  256.  
  257. void GC_reclaim()
  258. {
  259. register struct hblk *hbp;    /* ptr to current heap block        */
  260. register unsigned long index;
  261. # ifdef SEPARATE_HEADERS
  262.     struct hblkhdr * hhdr;
  263. # endif
  264. register int sz;        /* size of objects in current block    */
  265. register bool is_atomic;        /* => current block contains ptrfree objs */
  266. register char * GC_heapstart_reg = GC_heapstart;
  267. # define GC_heapstart GC_heapstart_reg
  268. # ifdef PRINTSTATS
  269.     long now_count = 0;
  270.     long defer_count = 0;
  271.     long skip_count = 0;
  272. # endif
  273.  
  274.  
  275. #   ifdef PRINTBLOCKS
  276.         GC_vprintf("reclaim: current block sizes:\n");
  277. #   endif
  278.  
  279. #   ifdef DEBUG
  280.     GC_vprintf("GC_heapstart = 0x%X, GC_heaplim = 0x%X\n", GC_heapstart,
  281.            GC_heaplim);
  282.     { int i;
  283.       for (i = 0;
  284.            i < ((long)GC_heaplim - (long)GC_heapstart) / HBLKSIZE;
  285.            i++) {
  286.           GC_vprintf("(%d)", hblkmap[i]);
  287.       }
  288.       GC_vprintf("\n");
  289.     }
  290. #   endif
  291.  
  292.   /* Go through all heap blocks (in hblklist) and reclaim unmarked objects */
  293.   /* For blocks containing small objects, we queue the block for later     */
  294.   /* reclamation.                                                          */
  295.     hbp = (struct hblk *) (((long) GC_heaplim - 1) & ~(HBLKMASK-1));
  296.     index = hbp - (struct hblk *) GC_heapstart;
  297.     while (index > 0) {
  298.       switch (hblkmap[index]) {
  299.         case HBLK_INVALID:
  300.           index--;
  301.           break;
  302.         case HBLK_VALID:
  303.           hbp = ((struct hblk *) GC_heapstart) + index;
  304. #      ifdef SEPARATE_HEADERS
  305.         hhdr = headers[index];
  306.         sz = hb_sz_from_hdr(hhdr);
  307. #        define SZ_LINK hb_sz_link_from_hdr(hhdr)
  308. #      else
  309.         sz = hb_sz(hbp);
  310. #        define SZ_LINK hb_sz_link(hbp)
  311. #      endif
  312.       if (sz < 0) {
  313.         sz = -sz;
  314.         is_atomic = TRUE;        /* this block contains atomic objs */
  315.       } else {
  316.         is_atomic = FALSE;
  317.       }
  318.       if( sz > MAXOBJSZ ) {  /* 1 big object */
  319. #        ifdef SEPARATE_HEADERS
  320.           register bool mb = mark_bit_from_hdr(hhdr, HDR_WORDS);
  321. #        else
  322.           register bool mb = mark_bit(hbp, HDR_WORDS);
  323. #        endif
  324.               
  325. #           ifdef PRINTSTATS
  326.         now_count++;
  327. #           endif
  328. #           ifdef PRINTBLOCKS
  329.           GC_vprintf("%d(%c,%c)",sz, (is_atomic? 'a' : 'c'),
  330.                  mb ? 'n' : 'e' );
  331. #           endif
  332.         if (!mb) {
  333.             GC_mem_found += sz;
  334.                 SZ_LINK = GC_freeable_blocks;
  335.                 GC_freeable_blocks = hbp;
  336.         }  /* end if (empty) */
  337.       } else { /* group of smaller objects */
  338.         if (!(hb_tenured(hbp))) {
  339. #             ifdef PRINTSTATS
  340.         defer_count++;
  341. #             endif
  342.           if (is_atomic) {
  343.         SZ_LINK = areclaim_list[sz];
  344.         areclaim_list[sz] = hbp;
  345.           } else {
  346.         SZ_LINK = reclaim_list[sz];
  347.         reclaim_list[sz] = hbp;
  348.           }
  349.         } else {
  350. #               ifdef PRINTSTATS
  351.           skip_count++;
  352. #               endif
  353.         }
  354.       }
  355.       index--;
  356.       break;
  357.     default:
  358.       index -= hblkmap[index];
  359.       } /* end switch */
  360.     } /* end for */
  361. #   ifdef PRINTSTATS
  362.       GC_printf(
  363.     "Directly reclaimed %d blocks, deferred %d blocks, skipped %d blocks\n",
  364.     now_count, defer_count, skip_count);
  365. #   endif
  366. # undef GC_heapstart
  367. }
  368.  
  369. /* revoke all tenure */
  370. void GC_revoke_all_tenure()
  371. {
  372. register struct hblk *hbp;    /* ptr to current heap block        */
  373. register long index;
  374.  
  375.   /* Go through all heap blocks (in hblklist) and untenure everyone  */
  376.     hbp = (struct hblk *) (((long) GC_heaplim - 1) & ~(HBLKMASK-1));
  377.     index = hbp - (struct hblk *) GC_heapstart;
  378.     while (index >= 0) {
  379.       switch (hblkmap[index]) {
  380.         case HBLK_INVALID:
  381.           index--;
  382.           break;
  383.         case HBLK_VALID:
  384.           hbp = ((struct hblk *) GC_heapstart) + index;
  385.           hb_tenured(hbp) = 0;
  386.       index--;
  387.       break;
  388.     default:
  389.       index -= hblkmap[index];
  390.       } /* end switch */
  391.     } /* end while */
  392. }
  393.  
  394. /* Reclaim all objects in heap block pointed to by given    */
  395. /* reclaim list entry, and remove the block from the list.  */
  396. /* The block is assumed to contain atomic objects.        */
  397. /* Assumes GC_allocate_ml is not held.  Acquires it.        */
  398. /* Results are discarded if there is any possible        */
  399. /* interference with another thread.                */
  400. void GC_reclaim_atomic(rlp)
  401. struct hblk **rlp;    /* ptr to reclaim list entry        */
  402. {
  403.     register struct hblk * hbp;
  404.     register int word_no;       /* Number of word in block              */
  405.     register long i;
  406.     register word *p;           /* pointer to current word in block     */
  407.     register int mb;            /* mark bit of current word             */
  408.     int sz;                     /* size of objects in current block     */
  409.     word *plim;
  410.     int empty;                  /* empty & done with block => block empty */
  411.     struct obj *list;           /* list of free objects in block         */
  412.     struct obj *last;           /* last free object on list              */
  413.     register int words_found = 0;
  414.     unsigned long init_inval_counter;
  415.     bool busy;
  416. #   ifdef SEPARATE_HEADERS
  417.     register struct hblkhdr * hhdr;
  418. #   endif
  419.  
  420.     if (I_HOLD_ML(&GC_allocate_ml)) {
  421.         XR_Panic("GC_reclaim_atomic 0");
  422.     }
  423. #   ifdef GATHERSTATS
  424.     GC_reclaim_count++;
  425. #   endif
  426.  
  427.     XR_MonitorEntry(&GC_allocate_ml);
  428.     /* Free blocks waiting to be restored to free list while we're in control */
  429.         GC_free_blocks(A_FEW);
  430.     hbp = *rlp;
  431.     if (hbp == NIL) {
  432.         XR_MonitorExit(&GC_allocate_ml);
  433.         return;
  434.     }
  435. #   ifdef VISUALIZE
  436.     /* Make sure it's not dimmed */
  437.     displayAllocHblk(hbp, BYTES_TO_WORDS(HBLKSIZE));
  438. #   endif
  439.     * rlp = hb_sz_link(hbp);
  440.     init_inval_counter = GC_reclaim_inval_counter;
  441. #   ifdef SEPARATE_HEADERS
  442.     hhdr = HDR(hbp);
  443.     sz = -(hb_sz_from_hdr(hhdr));
  444.     busy = hb_busy_from_hdr(hhdr);
  445. #   else
  446.         sz = -(hb_sz(hbp));
  447.         busy = hb_busy(hbp);
  448. #   endif
  449.     if (busy) {
  450.     GC_vprintf("Found busy block\n");
  451.     XR_MonitorExit(&GC_allocate_ml);
  452.         return;
  453.     }
  454.         
  455.     /* If this block looks like it will be tenured, avoid touching it. */
  456.       if (GC_hblk_probably_tenurable(hbp)
  457.           && GC_hblk_definitely_tenurable(hbp)) {
  458. #         ifdef GATHERSTATS
  459.         GC_tenure_count++;
  460. #         endif
  461.       hb_tenured(hbp) = 1;
  462.           XR_MonitorExit(&GC_allocate_ml);
  463.       return;
  464.       }
  465. #   ifdef SEPARATE_HEADERS
  466.     hb_busy_from_hdr(hhdr) = TRUE;
  467. #   else
  468.         hb_busy(hbp) = TRUE;
  469. #   endif
  470.       
  471.     XR_MonitorExit(&GC_allocate_ml);
  472.  
  473.     p = (word *)(hbp->hb_body);
  474.     word_no = ((word *)p) - ((word *)hbp);
  475.     plim = (word *)((((unsigned)hbp) + HBLKSIZE)
  476.        - WORDS_TO_BYTES(sz));
  477.  
  478.     last = (struct obj *)0;
  479.  
  480.     /* Look for first reclaimable object */
  481.     while( p <= plim && last == (struct obj *)0)  {
  482. #        ifndef SEPARATE_HEADERS
  483.           mb = mark_bit(hbp, word_no);
  484. #        else
  485.           mb = mark_bit_from_hdr(hhdr, word_no);
  486. #        endif
  487.  
  488.         if( mb ) {
  489. #               ifdef VISUALIZE
  490.             displayMark(p, sz);
  491. #               endif
  492.         p += sz;
  493.         } else {
  494.         words_found += sz;
  495.         /* word is available - put on list */
  496. #            ifdef DIRTY_BITS
  497.               /* We will write to this page.  This is likely to      */
  498.               /* generate a write fault.  Explicitly dirty the page  */
  499.               /* instead.                         */
  500.                 XR_PrepareForWriting(p);
  501. #            endif
  502.             set_obj_link((struct obj *)p, 0);
  503.             last = list = ((struct obj *)p);
  504. #               ifdef VISUALIZE
  505.             displayFree(p, sz);
  506. #               endif
  507.         p += sz;
  508.         }
  509.         word_no += sz;
  510.     }
  511.  
  512.     /* Look for remaining reclaimable objects */
  513.     while( p <= plim )  {
  514. #        ifndef SEPARATE_HEADERS
  515.           mb = mark_bit(hbp, word_no);
  516. #        else
  517.           mb = mark_bit_from_hdr(hhdr, word_no);
  518. #        endif
  519.  
  520.         if( mb ) {
  521. #               ifdef VISUALIZE
  522.             displayMark(p, sz);
  523. #               endif
  524.         p += sz;
  525.         } else {
  526.         words_found += sz;
  527.         /* object is available - put on list */
  528.             set_obj_link((struct obj *)p, list);
  529.             list = ((struct obj *)p);
  530. #               ifdef VISUALIZE
  531.             displayFree(p, sz);
  532. #               endif
  533.         p += sz;
  534.         }
  535.         word_no += sz;
  536.     }
  537.      
  538.  
  539.     empty = (p - (word *)(hbp->hb_body) == words_found);
  540.     /*
  541.      * if block has reachable words in it, we can't reclaim the
  542.      * whole thing so put list of free words in block back on
  543.      * free list for this size.
  544.      */
  545.  
  546. #    ifdef PRINTBLOCKS
  547.       GC_vprintf("%d(a,%c)",sz, (empty ? 'e' : 'n') );
  548. #    endif
  549.     XR_MonitorEntry(&GC_allocate_ml);
  550.     hb_busy(hbp) = FALSE;
  551.     if (init_inval_counter != GC_reclaim_inval_counter) {
  552.         GC_vprintf("Found incremented reclaim counter\n");
  553.         XR_MonitorExit(&GC_allocate_ml);
  554.         return;
  555.     }
  556.     if (empty) {
  557.         hb_uninit(hbp) = 1;
  558.         /* remove this block from list of active blocks and free it */
  559.         {
  560.           GC_del_hblklist(hbp);  
  561.           GC_freehblk(hbp);
  562.           GC_mem_found += words_found;
  563.         }
  564.     } else { /* ! empty */
  565.       /* We checked for tenurable above, but only checked for definitely
  566.          tenurable if the block was probably tenurable.  Thus, it may happen
  567.          that a block is not probably tenurable, but against all probability
  568.          is indeed tenurable.  That's what this test here is for.
  569.          */
  570.           if (!TENURE(words_found)) {
  571.           set_obj_link(last, aobjfreelist[sz]);
  572.           aobjfreelist[sz] = list;
  573.           GC_mem_found += words_found;
  574.           } else {
  575.                   /* Throw away what we found, since we don't */
  576.           /* want to keep dirtying the page.          */
  577. #                 ifdef GATHERSTATS
  578.               GC_tenure_count++;
  579. #                 endif
  580. #                 ifdef VISUALIZE
  581.               dim_page(hbp);
  582. #                 endif
  583.           hb_tenured(hbp) = 1;
  584.           }
  585.           hb_last_reclaimed(hbp) = GC_gc_no;
  586.     }
  587.     XR_MonitorExit(&GC_allocate_ml);    
  588. }
  589.  
  590. /* Reclaim all objects in heap block pointed to by given    */
  591. /* reclaim list entry, and remove the block from the list.  */
  592. /* The block is assumed to contain composite objects.        */
  593. /* Assumes GC_allocate_ml is not held.  Acquires it.        */
  594. /* Results are discarded if there is any possible        */
  595. /* interference with another thread.                */
  596. void GC_reclaim_composite(rlp)
  597. struct hblk **rlp;    /* ptr to current heap block        */
  598. {
  599.     register struct hblk * hbp;
  600.     register int word_no;       /* Number of word in block              */
  601.     register long i;
  602.     register word *p;           /* pointer to current word in block     */
  603.     register int mb;            /* mark bit of current word             */
  604.     int sz;                     /* size of objects in current block     */
  605.     word *plim;
  606.     int empty;                  /* empty & done with block => block empty */
  607.     struct obj *list;           /* list of free objects in block         */
  608.     struct obj *last;           /* last free object on list              */
  609.     register int words_found = 0;
  610.     unsigned long init_inval_counter;
  611.     bool busy;
  612. #   ifdef SEPARATE_HEADERS
  613.     register struct hblkhdr * hhdr;
  614. #   endif
  615.  
  616. #   ifdef GATHERSTATS
  617.     GC_reclaim_count++;
  618. #   endif
  619.  
  620.     XR_MonitorEntry(&GC_allocate_ml);
  621.     /* Free blocks waiting to be restored to free list while we're in control */
  622.         GC_free_blocks(A_FEW);
  623.     hbp = *rlp;
  624.     if (hbp == NIL) {
  625.         XR_MonitorExit(&GC_allocate_ml);
  626.         return;
  627.     }
  628. #   ifdef VISUALIZE
  629.     /* Make sure it's not dimmed */
  630.     displayAllocHblk(hbp, BYTES_TO_WORDS(HBLKSIZE));
  631. #   endif
  632.     * rlp = hb_sz_link(hbp);
  633.     init_inval_counter = GC_reclaim_inval_counter;
  634. #   ifdef SEPARATE_HEADERS
  635.     hhdr = HDR(hbp);
  636.     sz = hb_sz_from_hdr(hhdr);
  637.     busy = hb_busy_from_hdr(hhdr);
  638. #   else
  639.         sz = hb_sz(hbp);
  640.         busy = hb_busy(hbp);
  641. #   endif
  642.     if (busy) {
  643.     GC_vprintf("Found busy block\n");
  644.     XR_MonitorExit(&GC_allocate_ml);
  645.         return;
  646.     }
  647.  
  648.     /* If this block looks like it will be tenured, avoid touching it. */
  649.       if (GC_hblk_probably_tenurable(hbp)
  650.           && GC_hblk_definitely_tenurable(hbp)) {
  651. #         ifdef GATHERSTATS
  652.         GC_tenure_count++;
  653. #         endif
  654.       hb_tenured(hbp) = 1;
  655.           XR_MonitorExit(&GC_allocate_ml);
  656.       return;
  657.       }
  658.  
  659. #   ifdef SEPARATE_HEADERS
  660.     hb_busy_from_hdr(hhdr) = TRUE;
  661. #   else
  662.         hb_busy(hbp) = TRUE;
  663. #   endif
  664.       
  665.     XR_MonitorExit(&GC_allocate_ml);
  666.  
  667.     p = (word *)(hbp->hb_body);
  668.     word_no = ((word *)p) - ((word *)hbp);
  669.     plim = (word *)((((unsigned)hbp) + HBLKSIZE)
  670.        - WORDS_TO_BYTES(sz));
  671.  
  672.     last = (struct obj *)0;
  673.  
  674.     /* Look for first reclaimable object */
  675.     while( p <= plim && last == (struct obj *)0)  {
  676. #        ifndef SEPARATE_HEADERS
  677.           mb = mark_bit(hbp, word_no);
  678. #        else
  679.           mb = mark_bit_from_hdr(hhdr, word_no);
  680. #        endif
  681.  
  682.         if( mb ) {
  683. #               ifdef VISUALIZE
  684.             displayMark(p, sz);
  685. #               endif
  686.         p += sz;
  687.         } else {
  688.         words_found += sz;
  689. #               ifdef VISUALIZE
  690.             displayFree(p, sz);
  691. #               endif
  692.         /* word is available - put on list */
  693. #            ifdef DIRTY_BITS
  694.               /* We will write to this page.  This is likely to      */
  695.               /* generate a write fault.  Explicitly dirty the page  */
  696.               /* instead.                         */
  697.                 XR_PrepareForWriting(p);
  698. #            endif
  699.             set_obj_link((struct obj *)p, 0);
  700.             last = list = ((struct obj *)p);
  701.         /* Clear object, advance p to next object in the process */
  702.             i = (long)(p + sz);
  703.             p++; /* Skip link field */
  704.             while (p < (word *)i) {
  705.             *p++ = 0;
  706.             }
  707.         }
  708.         word_no += sz;
  709.     }
  710.  
  711.     /* Look for remaining reclaimable objects */
  712.     while( p <= plim )  {
  713. #        ifndef SEPARATE_HEADERS
  714.           mb = mark_bit(hbp, word_no);
  715. #        else
  716.           mb = mark_bit_from_hdr(hhdr, word_no);
  717. #        endif
  718.  
  719.         if( mb ) {
  720.         p += sz;
  721. #               ifdef VISUALIZE
  722.             displayMark(p, sz);
  723. #               endif
  724.         } else {
  725.         words_found += sz;
  726. #               ifdef VISUALIZE
  727.             displayFree(p, sz);
  728. #               endif
  729.         /* object is available - put on list */
  730.             set_obj_link((struct obj *)p, list);
  731.             list = ((struct obj *)p);
  732.         /* Clear object, advance p to next object in the process */
  733.             i = (long)(p + sz);
  734.             p++; /* Skip link field */
  735.             while (p < (word *)i) {
  736.                *p++ = 0;
  737.             }
  738.         }
  739.         word_no += sz;
  740.     }
  741.      
  742.  
  743.     empty = (p - (word *)(hbp->hb_body) == words_found);
  744.     /*
  745.      * if block has reachable words in it, we can't reclaim the
  746.      * whole thing so put list of free words in block back on
  747.      * free list for this size.
  748.      */
  749.  
  750. #    ifdef PRINTBLOCKS
  751.       GC_vprintf("%d(c,%c)",sz, (empty ? 'e' : 'n') );
  752. #    endif
  753.     XR_MonitorEntry(&GC_allocate_ml);
  754.     hb_busy(hbp) = FALSE;
  755.     if (init_inval_counter != GC_reclaim_inval_counter) {
  756.         GC_vprintf("Found incremented reclaim counter\n");
  757.         XR_MonitorExit(&GC_allocate_ml);
  758.         return;
  759.     }
  760.     if (empty) {
  761.         /* Clear words at beginning of objects */
  762.         /* Since most of it is already cleared */
  763.         p = (word *)(hbp->hb_body);
  764.         plim = (word *)((((unsigned)hbp) + HBLKSIZE)
  765.                - WORDS_TO_BYTES(sz));
  766.         while (p <= plim) {
  767.           *p = 0;
  768.           p += sz;
  769.         }
  770.         hb_uninit(hbp) = 0;
  771.  
  772.         /* remove this block from list of active blocks and free it */
  773.         {
  774.           GC_del_hblklist(hbp);  
  775.           GC_freehblk(hbp);
  776.           GC_mem_found += words_found;
  777.         }
  778.  
  779.     } else { /* ! empty */
  780.       /* Add to the appropriate free list, unless it's too full    */
  781.       /* We claim that we don't lose much if this gets interrupted */
  782.       /* in the middle.                                            */
  783.       /* We checked for turable above, but only checked for definitely
  784.          tenurable if the block was probably tenurable.  Thus, it may happen
  785.          that a block is not probably tenurable, but against all probability
  786.          is indeed tenurable.  That's what this test here is for.
  787.          */
  788.         if (!TENURE(words_found)) {
  789.           set_obj_link(last, objfreelist[sz]);
  790.           objfreelist[sz] = list;
  791.           GC_mem_found += words_found;
  792.           /* ...and here is where we need to fluff up the mark bits for
  793.              two-collection survival policy */
  794.         } else {
  795.                   /* Throw away what we found, since we don't */
  796.           /* want to keep dirtying the page.          */
  797. #                 ifdef GATHERSTATS
  798.               GC_tenure_count++;
  799. #                 endif
  800. #                 ifdef VISUALIZE
  801.               dim_page(hbp);
  802. #                 endif
  803.           hb_tenured(hbp) = 1;
  804.         }
  805.         hb_last_reclaimed(hbp) = GC_gc_no;
  806.     }
  807.     XR_MonitorExit(&GC_allocate_ml);
  808. }
  809.  
  810. /* Return an entire small object block to the free memory pool.     */
  811. /* it had better be empty.  Otherwise we make no assumptions.        */
  812. /* We try not to touch the block itself.                */
  813. void GC_reclaim_entire_block(hbp)
  814. register struct hblk *hbp;
  815. {
  816.     short sz = hb_sz(hbp);
  817.     
  818.     if (hb_busy(hbp)) {
  819.     GC_vprintf("GC_reclaim_entire_block encountered busy\n");
  820.     /* Some one is still busy reclaiming this from a previous     */
  821.     /* collection cycle, and may be writing a free list in it.    */
  822.     /* He'll eventually look at GC_reclaim_inval_counter and    */
  823.     /* discard it.  Then we'll catch it the next time.        */
  824.     return;
  825.     }
  826.     if (sz < 0) sz = -sz;
  827.     hb_uninit(hbp) = 1;
  828.     GC_del_hblklist(hbp);
  829.     GC_freehblk(hbp);
  830.     GC_mem_found += (BODY_SZ - (BODY_SZ % sz));
  831. }
  832.  
  833. /* Check whether a heap block is definitely empty.       */
  834. /* Hbp points to a block containing small objects.       */
  835. /* May (with tiny probability) identify an empty     */
  836. /* block as nonempty.                     */
  837. bool GC_hblk_empty(hbp)
  838. register struct hblk *hbp;    /* ptr to current heap block        */
  839. {
  840.     register long * m;
  841.     register long * marks = hb_marks(hbp);
  842.     register long * mark_lim = marks + MARK_BITS_SZ;
  843.  
  844.     /* Make a quick attempt at ignoring bogus mark bits at the beginning. */    
  845.       if (HDR_WORDS >= 16) {
  846.         if (HDR_WORDS >= 32) {
  847.             marks++;
  848.         } else {
  849.             *marks &= 0xffff0000;
  850.         }
  851.       }
  852.     for (m = marks; m < mark_lim; m++) {
  853.         if (*m) return(FALSE);
  854.     }
  855.  
  856.     return(TRUE);
  857. }
  858.  
  859. /* Check whether a heap block is likely to be tenurable.     */
  860. /* Hbp points to a block containing small objects.           */
  861. /* Only a hint.  May err on either side.             */
  862. bool GC_hblk_probably_tenurable(hbp)
  863. register struct hblk *hbp;    /* ptr to current heap block        */
  864. {
  865.     register int sz;            /* size of objects in current block     */
  866. #   define  start_word_no HDR_WORDS
  867.     register int nobjs;
  868.     register int middle_obj;
  869.     register int beginning_obj;
  870.  
  871.     sz = hb_sz(hbp);
  872.     if (sz < 0) sz = -sz;
  873.  
  874.     switch (sz) {
  875.         case 1:
  876.           nobjs = (BYTES_TO_WORDS(HBLKSIZE) - start_word_no);
  877.           beginning_obj = start_word_no + ((nobjs - 1) >> 2);
  878.           break;
  879.         case 2:
  880.           nobjs = (BYTES_TO_WORDS(HBLKSIZE) - start_word_no)/2;
  881.           beginning_obj = start_word_no + (((nobjs - 1) >> 2) << 1);
  882.           break;
  883.         case 4:
  884.           nobjs = (BYTES_TO_WORDS(HBLKSIZE) - start_word_no)/4;
  885.           beginning_obj = start_word_no + ((nobjs - 1) & ~3);
  886.           break;
  887.         case 6:
  888.           nobjs = (BYTES_TO_WORDS(HBLKSIZE) - start_word_no)/6;
  889.           beginning_obj = start_word_no + ((nobjs - 1) >> 2) * 6;
  890.           break;
  891.         case 8:
  892.           nobjs = (BYTES_TO_WORDS(HBLKSIZE) - start_word_no)/8;
  893.           beginning_obj = start_word_no + (((nobjs - 1) >> 2) << 3);
  894.           break;
  895.         case 16:
  896.           nobjs = (BYTES_TO_WORDS(HBLKSIZE) - start_word_no)/16;
  897.           beginning_obj = start_word_no + (((nobjs - 1) >> 2) << 4);
  898.           break;
  899.         default:
  900.           nobjs = IDIV((BYTES_TO_WORDS(HBLKSIZE) - start_word_no),sz);
  901.           beginning_obj = start_word_no + ((nobjs - 1) >> 2) * sz;
  902.     }
  903.  
  904.     if (!mark_bit(hbp, beginning_obj)) return(FALSE);
  905.     
  906.     middle_obj = /* start_word_no + ((nobjs - 1) >> 1) * sz */
  907.                  (beginning_obj << 1) - start_word_no;
  908.     if (!mark_bit(hbp, middle_obj)) return(FALSE);
  909.     
  910.     return(TRUE);
  911. }
  912.  
  913. /* Check whether a heap block is definitely tenurable.       */
  914. /* Hbp points to a block containing small objects.           */
  915. /* We do not dirty the heap block in question.               */
  916. bool GC_hblk_definitely_tenurable(hbp)
  917. register struct hblk *hbp;    /* ptr to current heap block        */
  918. {
  919.     register int word_no;       /* Number of word in block              */
  920.     register int mb;            /* mark bit of current word             */
  921.     register int sz;            /* size of objects in current block     */
  922. #   define  start_word_no HDR_WORDS
  923.     int nobjs;
  924.     register int n_empty = 0;     /* Number of reclaimable words. */
  925. #   ifdef SEPARATE_HEADERS
  926.     register struct hblkhdr * hhdr = HDR(hbp);
  927. #   endif
  928.  
  929.     sz = hb_sz(hbp);
  930.     if (sz < 0) sz = -sz;
  931.  
  932.     nobjs = IDIV((BYTES_TO_WORDS(HBLKSIZE) - start_word_no), sz);
  933.  
  934.     word_no = start_word_no + (nobjs - 1) * sz;
  935.  
  936.     /* Count empty words. */
  937.     while( word_no >= start_word_no )  {
  938. #        ifndef SEPARATE_HEADERS
  939.           mb = mark_bit(hbp, word_no);
  940. #        else
  941.           mb = mark_bit_from_hdr(hhdr, word_no);
  942. #        endif
  943.         if( ! mb ) {
  944.         n_empty += sz;
  945.         }
  946.         word_no -= sz;
  947.     }
  948.         return(TENURE(n_empty));
  949. }
  950.  
  951. #ifdef CHECK_INVARIANTS
  952. /*
  953. If we are going to check invariants, here's what we look for:
  954.   1) No root or register points to an unmarked object.
  955.   2) No marked object points to an unmarked object.
  956.  
  957. */
  958.  
  959. bool GC_valid_unmarked_object(p)
  960.      struct obj * p;
  961. {
  962.   struct hblk * h;
  963. # ifdef SEPARATE_HEADERS
  964.     struct hblkhdr * hhdr;
  965. # endif
  966.   long word_no;
  967.   int sz;
  968.   bool is_atomic;
  969.  
  970.   if (!quicktest(p)) return(FALSE);
  971. #     define CONTINUE return(FALSE)
  972. #     define CONTINUE_M return(FALSE)
  973. #     include "GCcheck_ptr.c"
  974. #     undef CONTINUE
  975. #     undef CONTINUE_M
  976.  
  977.   return (TRUE);
  978. }
  979.  
  980. void GC_print_object_stats(p)
  981.      struct obj * p;
  982. {
  983.   struct hblk * h;
  984. # ifdef SEPARATE_HEADERS
  985.     struct hblkhdr * hhdr;
  986. # endif
  987.   long word_no;
  988.   int sz;
  989.   bool is_atomic;
  990.  
  991.   if (!quicktest(p)) return;
  992. #     define CONTINUE return
  993. #     define CONTINUE_M return
  994. #     include "GCcheck_ptr.c"
  995. #     undef CONTINUE
  996. #     undef CONTINUE_M
  997.  
  998.   XR_ConsoleMsg("Object at 0x%x is size %d\n", p, sz);
  999. }
  1000.  
  1001. void GC_check_invariants_marked_object(h, word_no, sz, p, pageindex)
  1002.      struct hblk * h;
  1003.      long word_no;
  1004.      int sz;
  1005.      struct obj * p;
  1006.      unsigned long pageindex;
  1007. {
  1008.       /* check fields inside the object */
  1009.     register struct obj ** q;
  1010.     register struct obj * r;
  1011.     register long lim;   /* Should be struct obj **, but we're out of */
  1012.                  /* A registers on a 68000.                   */
  1013.  
  1014. #       ifdef INTERIOR_POINTERS
  1015.       /* Adjust p, so that it's properly aligned */
  1016.         p = ((struct obj *)(((word *)h) + word_no));
  1017. #       endif
  1018. #       ifdef UNALIGNED
  1019.       lim = ((long)(&(p -> obj_component[sz]))) - 3;
  1020. #       else
  1021.       lim = (long)(&(p -> obj_component[sz]));
  1022. #       endif
  1023.     for (q = (struct obj **)(&(p -> obj_component[0]));
  1024.                     q < (struct obj **)lim;) {
  1025.         r = *q;
  1026.         if (GC_valid_unmarked_object(r)) {
  1027.           XR_ConsoleMsg("*** pointer in 0x%x@0x%x[size=0x%x] ",
  1028.             p, q, sz);
  1029.           XR_ConsoleMsg("dirt[%d]=0x%x points to unmarked 0x%x\n",
  1030.             pageindex, GC_dirty_bits[pageindex], r);
  1031.         }
  1032. #           ifdef UNALIGNED
  1033.         q = ((struct obj **)(((long)q)+alignment));
  1034. #           else
  1035.         q++;
  1036. #           endif 
  1037.     }
  1038. }
  1039.  
  1040.  
  1041. void GC_check_invariants_before_reclaim_page(hbp, sz, pageindex)
  1042.     struct hblk * hbp;
  1043.     int sz;
  1044.     unsigned long pageindex;
  1045. {
  1046. #   ifndef SEPARATE_HEADERS
  1047.     this won't work
  1048. #   endif
  1049.  
  1050.     register word *p;           /* pointer to current word in block     */
  1051.     register int mb;            /* mark bit of current word             */
  1052.     register int mb2;            /* mark bit of current word             */
  1053.     register int word_no;       /* Number of word in block              */
  1054.     word *plim;
  1055.  
  1056.     p = (word *)(hbp->hb_body);
  1057.     word_no = ((word *)p) - ((word *)hbp);
  1058.     plim = (word *)((((unsigned)hbp) + HBLKSIZE)
  1059.        - WORDS_TO_BYTES(sz));
  1060.  
  1061.     if (sz > MAXOBJSZ) {
  1062.       /* one big object, one bunch of mark bits */
  1063.         mb = mark_bit(hbp, 0);
  1064.         if (mb) {
  1065.       GC_check_invariants_marked_object(hbp, word_no, sz, p, pageindex);
  1066.         }
  1067.     } else {
  1068.  
  1069.     for (p = (word *)(hbp->hb_body); p <= plim; (p += sz, word_no += sz)) {
  1070.         mb = mark_bit(hbp, word_no);
  1071.         if (mb) {
  1072.       GC_check_invariants_marked_object(hbp, word_no, sz, p, pageindex);
  1073.         }
  1074.       }
  1075.     }
  1076. }
  1077.  
  1078. void GC_check_invariants_before_reclaim_heap()
  1079. {
  1080. register struct hblk *hbp;    /* ptr to current heap block        */
  1081. register unsigned long index;
  1082. # ifdef SEPARATE_HEADERS
  1083.     struct hblkhdr * hhdr;
  1084. # endif
  1085. register int sz;        /* size of objects in current block    */
  1086. register bool is_atomic;        /* => current block contains ptrfree objs */
  1087. register char * GC_heapstart_reg = GC_heapstart;
  1088. # define GC_heapstart GC_heapstart_reg
  1089.  
  1090.   /* Go through all heap blocks (in hblklist) and check marked objects */
  1091.   /* For blocks containing small objects, we queue the block for later     */
  1092.   /* reclamation.                                                          */
  1093.     hbp = (struct hblk *) (((long) GC_heaplim - 1) & ~(HBLKMASK-1));
  1094.     index = hbp - (struct hblk *) GC_heapstart;
  1095.     while (index > 0) {
  1096.       switch (hblkmap[index]) {
  1097.         case HBLK_INVALID:
  1098.           index--;
  1099.           break;
  1100.         case HBLK_VALID:
  1101.           hbp = ((struct hblk *) GC_heapstart) + index;
  1102. #      ifdef SEPARATE_HEADERS
  1103.         hhdr = headers[index];
  1104.         sz = hb_sz_from_hdr(hhdr);
  1105. #        define SZ_LINK hb_sz_link_from_hdr(hhdr)
  1106. #      else
  1107.         sz = hb_sz(hbp);
  1108. #        define SZ_LINK hb_sz_link(hbp)
  1109. #      endif
  1110.       if (sz > 0) {
  1111.         /* non-atomic objects.  pointers to check. */
  1112.         GC_check_invariants_before_reclaim_page(hbp, sz, index);
  1113.       }
  1114.       index--;
  1115.       break;
  1116.     default:
  1117.       index -= hblkmap[index];
  1118.       } /* end switch */
  1119.     } /* end for */
  1120. # undef GC_heapstart
  1121. }
  1122.  
  1123. static unsigned long bad_ptr;
  1124.  
  1125. void GC_check_seg(start, end)
  1126. unsigned long start, end;
  1127. {
  1128.     register unsigned long *p;
  1129.     int dummy;
  1130.     unsigned long bad = (bad_ptr ^ 0x55555555);
  1131.     unsigned long stack_addr = (unsigned long)(&dummy);
  1132.     
  1133.     if (stack_addr > start - 0x2000 && stack_addr < end) {
  1134.         /* I'm looking at my own stack, which will contain bogus */
  1135.         /* references to the pointer I'm looking for.  This      */
  1136.         /* isn't interesting.                     */
  1137.         return;
  1138.     }
  1139.     for (p = (unsigned long *)start; p < (unsigned long *)end; p++) {
  1140.         if (*p == bad) {
  1141.             XR_ConsoleMsg("0x%X -> 0x%X\n", p, *p);
  1142.         }
  1143.     }
  1144. }
  1145.  
  1146. void GC_find_root_refs(p)
  1147. unsigned long p;
  1148. {
  1149.     struct data_list *d, *XR_get_data_list();
  1150.     int i;
  1151.     
  1152.     bad_ptr = (p ^ 0x55555555);
  1153.     XR_ConsoleMsg("%? Data and Bss segments for loaded modules:\n");
  1154.     for (i=0; d = XR_get_data_list(i); i++) {
  1155.     GC_check_seg(d->start, d->end);
  1156.     }
  1157.     XR_ConsoleMsg("Other roots:\n");
  1158.     XR_ApplyToSysMem(GC_check_seg, 0);
  1159.     bad_ptr = 0;
  1160. }
  1161.  
  1162. void GC_check_invariants_before_reclaim_roots()
  1163. {
  1164.   register struct hblk * h;
  1165.   register struct obj *p; /* pointer to current object to be marked */
  1166.  
  1167.  
  1168.   GC_mark_roots(ALL_POINTERS);
  1169.   
  1170.   while (GC_mark_stack_top != GC_mark_stack_bottom) {
  1171.     register struct hblk * h;
  1172. # ifdef SEPARATE_HEADERS
  1173.     register struct hblkhdr * hhdr;
  1174. # endif
  1175.     register long word_no;
  1176.     register int sz;
  1177.     register bool is_atomic;
  1178. #    ifdef USE_STACK
  1179.     p = (struct obj *)(*GC_mark_stack_top++);
  1180. #    else
  1181. #     ifdef USE_HEAP
  1182.     p = (struct obj *)(*GC_mark_stack_top--);
  1183. #     else
  1184.     --> fixit <--
  1185. #     endif
  1186. #    endif
  1187.  
  1188.       if (GC_valid_unmarked_object(p)) {
  1189.     XR_ConsoleMsg("*** Pointer to unmarked 0x%x found in roots.\n", p);
  1190.     GC_find_root_refs(p);
  1191.       }
  1192.    }    
  1193. }
  1194.  
  1195. void GC_check_invariants_before_reclaim()
  1196. {
  1197.   GC_check_invariants_before_reclaim_roots();
  1198.   GC_check_invariants_before_reclaim_heap();
  1199. }
  1200.  
  1201. # define MAXFRAMES 20
  1202. unsigned GC_saved_stack[MAXFRAMES];
  1203. # define NONE 0
  1204.  
  1205. /* Trace back through the stack starting at sp and save all pc values */
  1206. /* in stack.                                 */
  1207. void GC_save_stack(sp)
  1208. register unsigned sp;
  1209. {
  1210.     register int i = 0;
  1211.     register unsigned nsp;
  1212. #   define MAXDIF 64*1024
  1213.  
  1214.     if (sp < (unsigned)GC_heaplim
  1215.         || sp > (unsigned)GC_heaplim + 0x10000000) goto finish;
  1216.     while(i < MAXFRAMES) {
  1217.         GC_saved_stack[i++] = ((unsigned *)sp)[15];
  1218.         nsp = ((unsigned *)sp)[14];
  1219.         if(nsp < sp || nsp > sp + MAXDIF) break;
  1220.         sp = nsp;
  1221.     }
  1222.   finish:
  1223.     while(i < MAXFRAMES) {
  1224.         GC_saved_stack[i++] = NONE;
  1225.     }
  1226. }
  1227. void GC_print_stack()
  1228. {
  1229.     register int i;
  1230.     
  1231.     for (i = 0; i < MAXFRAMES; i++) {
  1232.         if (GC_saved_stack[i] == NONE) break;
  1233.         XR_ConsoleMsg("0x%X, ", GC_saved_stack[i]);
  1234.     }
  1235.     XR_ConsoleMsg(" ...\n");
  1236. }
  1237.  
  1238. void GC_print_thread_stack(i)
  1239. int i;
  1240. {
  1241.     unsigned long sp =
  1242.                   XR_sysArea -> sa_threadPool[i].t_resume.jb_data[2];
  1243.     unsigned long pc =
  1244.                   XR_sysArea -> sa_threadPool[i].t_resume.jb_data[0];
  1245.  
  1246.     GC_save_stack(sp);
  1247.     XR_ConsoleMsg("Thread 1 pc = 0x%X, sp = 0x%X\n", pc, sp);
  1248.     GC_print_stack();
  1249. }
  1250. #endif
  1251.